package com.dalonedrow.engine.sprite.base;

import java.util.HashMap;

import com.dalonedrow.engine.sprite.impl.GlyphObjectFactory;
import com.dalonedrow.engine.systems.JOGLErrorHandler;
import com.dalonedrow.pooled.PooledStringBuilder;
import com.dalonedrow.pooled.StringBuilderPool;

/**
 * Encapsulates a monospaced fontsheet containing the glyphs used for a font.
 * @author DaLoneDrau
 */
public final class FontsheetObject {
	/** the number of characters per row on a font sheet. */
	private static final int	CHAR_PER_ROW	= 14;
	/** the character map. */
	private static char[]		charMap;
	static {
		PooledStringBuilder sb =
				StringBuilderPool.getInstance().getStringBuilder();
		sb.append("abcdefgABCDEFG");
		sb.append("hijklmnHIJKLMN");
		sb.append("opqrstuOPQRSTU");
		sb.append("vwxyz  VWXYZ  ");
		sb.append("1234567890    ");
		sb.append(".:,;'\"(!?)    ");
		sb.append("+-*/=         ");
		sb.append("~`@#$%^&[]{}|\\");
		FontsheetObject.charMap = sb.toString().toCharArray();
		sb.returnToPool();
	}
	/**
	 * Gets the charMap.
	 * @return String
	 */
	public static char[] getCharMap() {
		return FontsheetObject.charMap;
	}
	/**
	 * the font's baseline - the number of pixels up from which the line that
	 * the letters "sit" upon.
	 */
	private int		baseline;
	/** the name of the current font. */
	private String	fontName;
	/** the id of the current sprite. */
	private char	glyph;
	/** if true, the font is monospaced; otherwise it is variable spaced. */
	private boolean	monospaced;
	/** the name of the texture. */
	private int		textureId;
	/**
	 * Creates a new instance of {@link FontsheetObject}.
	 * @param fName the name of the font being set
	 * @param charSize the size of a single character
	 * @param textureSize the size of the parent texture
	 * @param tId the id of the texture this font is derived from
	 * @param base the font's baseline
	 * @param offsets the x and y offsets from where the font starts on the
	 *        texture
	 */
	public FontsheetObject(final String fName, final SimpleDimension charSize,
			final SimpleDimension textureSize, final int tId, final int base,
			final int... offsets) {
		fontName = fName;
		textureId = tId;
		monospaced = true;
		baseline = base;
		boolean spaceDone = false;
		for (int i = 0; i < FontsheetObject.charMap.length; i++) {
			char c = FontsheetObject.charMap[i];
			if (c == ' ' && spaceDone) {
				continue;
			}
			if (c == ' ' && !spaceDone) {
				spaceDone = true;
			}
			int x = i % FontsheetObject.CHAR_PER_ROW;
			int y = i / FontsheetObject.CHAR_PER_ROW;
			x *= charSize.width;
			y *= charSize.height;
			try {
				SpriteImageObject image = new SpriteImageObject(
						offsets[0] + x, // left
						offsets[1] + y, // top
						charSize.width, // width
						charSize.height, // height
						textureSize, // parent texture size
						textureId, // texture name
						SpriteImageObjectFactory.getInstance().getNextId()
						);
				PooledStringBuilder sb =
						StringBuilderPool.getInstance().getStringBuilder();
				sb.append(fontName);
				sb.append("_");
				sb.append(c);
				SpriteImageObjectFactory.getInstance().addImage(sb.toString(),
						image);
				sb.returnToPool();
				GlyphObjectFactory.getInstance().addGlyph(fontName, c,
						image);
			} catch (Exception ex) {
				JOGLErrorHandler.getInstance().fatalError(ex);
			}
		}
	}
	/**
	 * Creates a new instance of Spritesheet.java.
	 * @param fName the name of the font being set
	 * @param newGlyphMap the map of character sizes
	 * @param textureSize the size of the parent texture
	 * @param tId the name of the texture this font is derived from
	 * @param base the font's baseline
	 * @param offsets the x and y offsets from where the font starts on the
	 *        texture
	 */
	public FontsheetObject(final String fName,
			final HashMap<Character, SimpleDimension> newGlyphMap,
			final SimpleDimension textureSize, final int tId, final int base,
			final int... offsets) {
		fontName = fName;
		textureId = tId;
		monospaced = false;
		baseline = base;
		boolean spaceDone = false;
		int x = 0, y = 0;
		for (int i = 0; i < FontsheetObject.charMap.length; i++) {
			char c = FontsheetObject.charMap[i];
			if (i > 0 && i % FontsheetObject.CHAR_PER_ROW == 0) {
				y += newGlyphMap.get(' ').height;
				x = 0;
			}
			if (c == ' ' && spaceDone) {
				x += newGlyphMap.get(c).width;
				continue;
			}
			if (c == ' ' && !spaceDone) {
				spaceDone = true;
			}
			try {
				SpriteImageObject image = new SpriteImageObject(
						offsets[0] + x, // left
						offsets[1] + y, // top
						newGlyphMap.get(c).width, // width
						newGlyphMap.get(c).height, // height
						textureSize, // parent texture size
						textureId, // texture name
						SpriteImageObjectFactory.getInstance().getNextId()
						);
				PooledStringBuilder sb =
						StringBuilderPool.getInstance().getStringBuilder();
				sb.append(fontName);
				sb.append("_");
				sb.append(c);
				SpriteImageObjectFactory.getInstance().addImage(sb.toString(),
						image);
				sb.returnToPool();
				GlyphObjectFactory.getInstance().addGlyph(fontName, c,
						image);
			} catch (Exception ex) {
				JOGLErrorHandler.getInstance().fatalError(ex);
			}
			x += newGlyphMap.get(c).width;
		}
	}
	/**
	 * Creates a new instance of <code>FontsheetObject</code>.
	 * @param fName the name of the font being set
	 * @param charWidth the width of a single character
	 * @param charHeight the height of a single character
	 * @param textureSize the size of the parent texture
	 * @param tId the name of the texture this font is derived from
	 * @param base the font's baseline
	 * @param offsets the x and y offsets from where the font starts on the
	 *        texture
	 */
	public FontsheetObject(final String fName, final int charWidth,
			final int charHeight, final SimpleDimension textureSize, 
			final int tId, final int base, final int... offsets) {
		this(fName, new SimpleDimension(charWidth, charHeight), textureSize,
				tId, base, offsets);
	}
	/**
	 * Gets the font's baseline.
	 * @return <code>int</code>
	 */
	public int getBaseline() {
		return baseline;
	}
	/**
	 * Gets the bottom coordinates for the the character being read from the
	 * {@link FontsheetObject}.
	 * @return <code>float</code>
	 * @throws Exception if an error occurs
	 */
	public float getBottom() throws Exception {
		return GlyphObjectFactory.getInstance().getGlyph(
				fontName, glyph).getBottom();
	}
	/**
	 * Gets the sprite's height.
	 * @return <code>int</code>
	 * @throws Exception if the current character wasn't set
	 */
	public int getCharHeight() throws Exception {
		return GlyphObjectFactory.getInstance().getGlyph(fontName, glyph)
				.getHeight();
	}
	/**
	 * Gets the sprite's width.
	 * @return <code>int</code>
	 * @throws Exception if the current character wasn't set
	 */
	public int getCharWidth() throws Exception {
		return GlyphObjectFactory.getInstance().getGlyph(
				fontName, glyph).getWidth();
	}
	/**
	 * Gets the current font name.
	 * @return {@link String}
	 */
	public String getFontName() {
		return fontName;
	}
	/**
	 * Gets the character being read from the {@link FontsheetObject}.
	 * @return char
	 */
	public char getGlyph() {
		return glyph;
	}
	/**
	 * Gets the left coordinates for the the character being read from the
	 * {@link FontsheetObject}.
	 * @return <code>float</code>
	 * @throws Exception if an error occurs
	 */
	public float getLeft() throws Exception {
		return GlyphObjectFactory.getInstance().getGlyph(
				fontName, glyph).getLeft();
	}
	/**
	 * Gets the maximum height and width for a character in the font.
	 * @return {@link SimpleDimension}
	 * @throws Exception if an error occurs
	 */
	public SimpleDimension getMaxCharMetrics() throws Exception {
		SimpleDimension d = null;
		if (monospaced) {
			int w = GlyphObjectFactory.getInstance().getGlyph(
					fontName, 'a').getWidth();
			int h = GlyphObjectFactory.getInstance().getGlyph(
					fontName, 'a').getHeight();
			d = new SimpleDimension(w, h);
		} else {
			int w = 0, h = 0;
			for (int i = 0; i < FontsheetObject.charMap.length; i++) {
				char c = FontsheetObject.charMap[i];
				w = Math.max(w, GlyphObjectFactory.getInstance().getGlyph(
						fontName, c).getWidth());
				h = Math.max(h, GlyphObjectFactory.getInstance().getGlyph(
						fontName, c).getHeight());
			}
			d = new SimpleDimension(w, h);
		}
		return d;
	}
	/**
	 * Gets the right coordinates for the the character being read from the
	 * {@link FontsheetObject}.
	 * @return <code>float</code>
	 * @throws Exception if an error occurs
	 */
	public float getRight() throws Exception {
		return GlyphObjectFactory.getInstance().getGlyph(
				fontName, glyph).getRight();
	}
	/**
	 * Gets the total height in pixels of a string of text.
	 * @param text the text
	 * @return <code>int</code>
	 * @throws Exception if an error occurs
	 */
	public int getTextHeight(final String text) throws Exception {
		int height = 0;
		for (int i = 0; i < text.length(); i++) {
			setGlyph(text.charAt(i));
			height = Math.max(height, getCharHeight());
		}
		return height;
	}
	/**
	 * Gets the id of the texture this font is rendered from.
	 * @return <code>int</code>
	 */
	public int getTextureId() {
		return textureId;
	}
	/**
	 * Gets the total width in pixels of a string of text.
	 * @param text the text
	 * @return <code>int</code>
	 * @throws Exception if an error occurs
	 */
	public int getTextWidth(final String text) throws Exception {
		int width = 0;
		for (int i = 0; i < text.length(); i++) {
			setGlyph(text.charAt(i));
			width += getCharWidth();
		}
		return width;
	}
	/**
	 * Gets the total width in pixels of a string of text, not including spaces.
	 * @param text the text
	 * @return <code>int</code>
	 * @throws Exception if an error occurs
	 */
	public int getTextWidthNoSpaces(final String text) throws Exception {
		int width = 0;
		for (int i = 0; i < text.length(); i++) {
			if (text.charAt(i) == ' ') {
				continue;
			}
			setGlyph(text.charAt(i));
			width += getCharWidth();
		}
		return width;
	}
	/**
	 * Gets the top coordinates for the the character being read from the
	 * {@link FontsheetObject}.
	 * @return <code>float</code>
	 * @throws Exception if an error occurs
	 */
	public float getTop() throws Exception {
		return GlyphObjectFactory.getInstance().getGlyph(
				fontName, glyph).getTop();
	}
	/**
	 * Gets the flag indicating whether the font is monospaced or not.
	 * @return if true, the font is monospaced; otherwise it is variable spaced
	 */
	public boolean isMonospaced() {
		return monospaced;
	}
	/**
	 * Sets the character being read from the {@link FontsheetObject}.
	 * @param character the character of the sprite
	 */
	public void setGlyph(final char character) {
		glyph = character;
	}
}
